package com.martin.lib_base.net.interceptor

import android.util.Base64
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.martin.lib_base.BuildConfig
import com.martin.lib_base.LibBase
import com.martin.lib_base.base.BaseResponse
import com.martin.lib_base.bean.ServiceEncryptedResponse
import com.martin.lib_base.bean.UserBean
import com.martin.lib_base.constant.ConstantKey
import com.martin.lib_base.ktx.logd
import com.martin.lib_base.ktx.loge
import com.martin.lib_base.ktx.logi
import com.martin.lib_base.ktx.logw
import com.martin.lib_base.ktx.toJson
import com.martin.lib_base.net.NetApply
import com.tencent.mmkv.MMKV
import dev.utils.common.RandomUtils
import dev.utils.common.encrypt.MD5Utils
import okhttp3.*
import okhttp3.ResponseBody.Companion.toResponseBody
import okio.internal.commonToUtf8String
import org.json.JSONObject
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * @author：Houge
 * @date：2022/9/15
 * @desc：签名及加解密 拦截器 - 直播框架的加密方式
 */
class SignAndEncryptionInterceptorLive : Interceptor {

    //签名 Key
    private val _signKey = if (BuildConfig.env == ConstantKey.RELEASE)
        BuildConfig.APP_SIGN_KEY
    else
        BuildConfig.APP_SIGN_KEY_TEST

    //AES 加密Key
    private val _aesKey = if (BuildConfig.env == ConstantKey.RELEASE)
        BuildConfig.APP_AES_KEY
    else
        BuildConfig.APP_AES_KEY_TEST

    /**
     * 是否需要签名并加密
     * 否则只需要签名
     */
    private val needSignAndEncryption = BuildConfig.ENCRYPT_TYPE == ConstantKey.RELEASE


    //不参与生成签名的字段
    private val _excludeSignKey = listOf("service")

    //加密后的数据Key
    private val _encryptDataKey = "encryptedData"

    /**
     * 拦截事件
     */
    override fun intercept(chain: Interceptor.Chain): Response {
        // 对请求进行加密,生成加密请求
        val request = signAndEncryptRequest(chain.request())

        // 发起加密请求
        val response = chain.proceed(request)

        //判断请求是否成功,是否有返回体
        if (response.isSuccessful && needSignAndEncryption) {
            return decryptResponse(response)
        }

        return response
    }

    /**
     * 解密返回参数
     */
    private fun decryptResponse(response: Response): Response {
        val body = response.body

        try {
            body.source().request(Long.MAX_VALUE)
            val buffer = body.source().buffer
            //获取接口数据
            val bodySource = buffer.clone().readUtf8()
//            logd("response --- 处理前未格式化 $bodySource")
//            logi("response --- 处理前的数据 ${JSONObject(bodySource).toString(2)}")
            //如果data字段数据不是Object,则剔除,避免后续判定错误
            //bodySource = cullErrorTypeData(bodySource, "data")
            //获取服务端返回的数据模型
            val serviceResponse =
                NetApply.gson.fromJson(bodySource, ServiceEncryptedResponse::class.java)

            return if (serviceResponse.ret == 200 && serviceResponse.data != null && serviceResponse.data.encryptedData != null) {
                //最外层数据判定成功,并且data中的加密数据存在,则处理解密流程
                val json = doAESDecrypt(serviceResponse.data.encryptedData)
                logw("------------------- response success data -------------------\n${JSONObject(json).toString(2)}")
                response.newBuilder().body(json.toResponseBody(body.contentType())).build()
            } else {
                //如果最外层数据报错,则创建内层数据
                val baseResponse = BaseResponse<Any>().apply {
                    code = serviceResponse.ret
                    msg = serviceResponse.msg
                }
                val json = NetApply.gson.toJson(baseResponse).trim()
                //loge("------------------- response error data ------------------- $json")
                response.newBuilder().body(json.toResponseBody(body.contentType())).build()
            }
        } catch (e: Exception) {

            val baseResponse = BaseResponse<Any>().apply {
                code = 999
                msg = "接口数据解析异常 ${e.message}"
            }
            val json = NetApply.gson.toJson(baseResponse).trim()
            return response.newBuilder().body(json.toResponseBody(body.contentType())).build()
        }
    }

    /**
     * 执行AES解密
     */
    private fun doAESDecrypt(encryptData: String): String {
        //偏移数据
        val iv = encryptData.substring(0, 16)
        //待解密数据
        val decryptData = encryptData.substring(16)
        //生成AES解码器
        val cipher = generateAESCipher(iv, false)
        //返回解码结果
        val doFinal =
            cipher.doFinal(Base64.decode(decryptData, Base64.NO_WRAP)).commonToUtf8String()

        return doFinal
    }

    /**
     * 签名并加密请求
     * 默认只有POST请求，如果后面新增其他类型，需要额外增加逻辑
     */
    private fun signAndEncryptRequest(request: Request): Request {
        if (request.method != "POST"
            || request.body == null
            || request.header("pass") == "skipAddToken"
            || request.body?.contentType()?.toString()
                ?.contains(MultipartBody.FORM.toString()) == true
        ) {
            return request
        }

        val body = request.body!!
        //生成签名
        val sign = generateSign(body)
        //生成加密用的JSON对象
        val encryptJsonObject = generateEncryptJsonObject(sign, body)
        //对Json对象进行加密,生成密文

        val aesEncrypt = doAESEncrypt(encryptJsonObject)

        logi("request ------ 加密后的参数  $aesEncrypt")

        //生成新的请求体
        val formBody = FormBody.Builder().apply {
            add(_encryptDataKey, aesEncrypt)
        }.build()

        //返回新的请求
        return request.newBuilder()
            .post(formBody)
            .build()
    }

    /**
     * 做AES加密
     */
    private fun doAESEncrypt(jsonObject: JsonObject): String {
        //生成随机字符串
        val iv = RandomUtils.getRandomNumbersAndLetters(16)
        //获取AES加密器
        val cipher = generateAESCipher(iv)
        //数据加密
        val doFinal = cipher.doFinal(jsonObject.toString().toByteArray())
        //转Base64
        val cipherText = Base64.encodeToString(doFinal, Base64.NO_WRAP)

        return "$iv$cipherText"
    }

    /**
     * 生成AES
     * @param isEncrypt 是否为加密模式,反之为解密
     */
    private fun generateAESCipher(iv: String, isEncrypt: Boolean = true): Cipher {
        //获取AES加密器
        val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
        //初始化秘钥
        cipher.init(
            if (isEncrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE,
            SecretKeySpec(_aesKey.toByteArray(), "AES"),
            IvParameterSpec(iv.toByteArray())
        )
        return cipher
    }

    /**
     * 生成加密用的对象
     */
    private fun generateEncryptJsonObject(sign: String, body: RequestBody): JsonObject {
        val jsonObject = getJsonObjectFromRequestBody(body)
        //将签名加入JSON中
        jsonObject.addProperty("sign", sign)

        /*
            登录状态下,拼接用户信息
            uid & token
         */
        if (LibBase.userId.isNotEmpty() && LibBase.token.isNotEmpty()) {
            if (!jsonObject.keySet().contains("uid")) {
                jsonObject.addProperty("uid", LibBase.userId)
            }
            if (!jsonObject.keySet().contains("token")) {
                jsonObject.addProperty("token", LibBase.token)
            }
        } else if (MMKV.defaultMMKV().decodeBool(ConstantKey.IS_LOGIN, false)) {
            MMKV.defaultMMKV()
                .decodeParcelable(ConstantKey.USER_INFO, UserBean::class.java)?.let {
                    if (!jsonObject.keySet().contains("uid")) {
                        jsonObject.addProperty("uid", it.id)
                    }
                    if (!jsonObject.keySet().contains("token")) {
                        jsonObject.addProperty("token", it.token)
                    }
                }
        }
        logi("request ------ 拼接后的数据  ${jsonObject.toJson()}")

        return jsonObject
    }

    /**
     * 生成签名
     */
    private fun generateSign(body: RequestBody): String {
        val sb = StringBuilder()
        val jsonObject = getJsonObjectFromRequestBody(body)

        //拼接参数
        jsonObject.keySet().forEach {
            if (!_excludeSignKey.contains(it)) {
                if (sb.isNotEmpty()) {
                    sb.append("&")
                }
                sb.append("$it=${jsonObject.get(it)}")
            }
        }

        /*
            登录状态下,拼接用户信息
            uid & token
         */
        if (LibBase.isLogin.value == true && LibBase.userInfo.value != null) {
            /*
                参数中不包含 uid,则认为默认由拦截器统一处理
             */
            if (!jsonObject.keySet().contains("uid")) {
                if (sb.isNotEmpty()) {
                    sb.append("&")
                }
                sb.append("uid=${LibBase.userInfo.value!!.id}")
            }
            /*
                参数中不包含token,则默认由拦截器统一处理
             */
            if (!jsonObject.keySet().contains("token")) {
                if (sb.isNotEmpty()) {
                    sb.append("&")
                }
                sb.append("token=${LibBase.token}")
            }
        }

        //拼接签名Key
        if (sb.isNotEmpty()) {
            sb.append("&")
        }
        sb.append(_signKey)

        val signStr = sb.toString().replace("\"", "")
        logw("请求签名: $signStr")
        return MD5Utils.md5(signStr)
    }

    /**
     * 剔除错误类型数据
     */
    private fun cullErrorTypeData(
        objString: String,
        key: String,
        isObject: Boolean = true
    ): String {
        val jsonObject = JSONObject(objString)
        val dataForKey = jsonObject.getString(key)
        if (!dataForKey.startsWith(if (isObject) "{" else "[")) {
            jsonObject.remove(key)
        }
        return jsonObject.toString()
    }

    /**
     * 获取[JsonObject] 根据[RequestBody]生成
     */
    private fun getJsonObjectFromRequestBody(body: RequestBody): JsonObject {
        return Gson().fromJson(okio.Buffer().apply {
            body.writeTo(this)
        }.readUtf8(), JsonObject::class.java)
    }
}